Previous Book Contents Book Index Next

Inside Macintosh: QuickDraw GX Objects /
Chapter 7 - View-Related Objects / Using View-Related Objects


Using View Ports

This section demonstrates how to use QuickDraw GX view ports. It shows how you can

Creating and Manipulating View Port Objects

QuickDraw GX provides several functions with which you can create a new view port. To create a view port that is the root view port of a hierarchy and is attached to
a Macintosh window, you use the function GXNewWindowViewPort, described in the Macintosh environment chapter of Inside Macintosh: QuickDraw GX Environment and Utilities. To create child view ports for that root parent, or to create a root view port for offscreen drawing, you use the GXNewViewPort function. You can also create a new view port object by copying an existing one with the GXCopyToViewPort function; see Listing 7-2 on page 7-44 for an example of this method.

Replacing the default view port
If your application draws only within windows, you may want to replace the default transform object (which references the default view port) for each shape type. You could replace it with a transform object that directly references a window view port (a view port attached to a Macintosh window). Alternatively, you could replace it with a transform that references a view port you have designated as the "current" view port; you could then redirect drawing to a window view port by assigning the window view port as the parent of the current view port. Or you could take a different approach and explicitly assign a child view port of a window view port to each shape as it is created, using the GXSetShapeViewPorts function.
Once you have created a view port object, you can customize its features using the techniques described in the following section.

You can test if two view port-object references refer to the same view port object by simply testing the references for equality. You can also test a view port for equality with another view port with the GXEqualViewPort function. For two view port objects to be equal, their mappings, clips, dithers, halftones, attributes, parent view ports, and view groups must be identical; if one view port is attached to a window, the other view port must be attached to the same window. The tag lists or child view ports of the view ports need not be identical. View port object copies created with the GXCopyToViewPort function are always equal to the view port from which they were copied.

To delete your application's reference to a view port object, call the GXDisposeViewPort function. Because view port objects have no owner count,
calling GXDisposeViewPort actually releases the memory allocated for that view port object, and invalidates all other references to it. Therefore, once you have disposed
of a view port, other transform objects that reference that view port will have invalid view port references in their view port lists. This causes no error when you try to draw; drawing simply does not occur to view ports whose references are invalid.

The following code fragment first creates a Macintosh window (sampleWindow), and then uses GXNewWindowViewPort to create a view port attached to it. When it no longer needs them, the code disposes of the view port and then the window. The NewWindow function and its parameters, and the DisposeWindow function, are described in the Window Manager chapter of Inside Macintosh: Macintosh Toolbox Essentials.

sampleWindow = NewWindow( nil, &windowRect, "\p", true, 
                        documentProc, (WindowPtr)-1L, true, 0L );
aViewPort = GXNewWindowViewPort(sampleWindow);
.
.  /* use the window and view port */
.
GXDisposeViewPort(aViewPort);
DisposeWindow(sampleWindow);
The following line of code creates a view port that is not attached to a window. You might use this call to create a view port that is to be the child of another view port. The code assigns the new view port to the gxScreenViewDevices view group, the view group for all onscreen drawing:

myChildViewPort =  GXNewViewPort(gxScreenViewDevices);
A more general way to assign the view group parameter is to first call the GXGetViewPortViewGroup function to determine the view group of the intended parent view port, and use that result as the parameter for GXNewViewPort. See, for example, Listing 7-5 on page 7-47.

The GXNewViewPort function is described on page 7-70. The GXCopyToViewPort function is described on page 7-72. The GXEqualViewPort function is described on page 7-73. The GXDisposeViewPort function is described on page 7-71.

Manipulating View Port Object Properties

This section describes how to manipulate the dither, halftone, view group, attributes, and tag list properties of a view port object:

How to manipulate other view port properties is described in subsequent sections, starting with "Getting and Setting a View Port's Clip and Mapping" on page 7-44.

Getting and Setting a View Port's Dither, Halftone, and Attributes

Listing 7-1 is an example of code that sets the dither level, halftone structure, and view port attributes of the view port myViewPort. For the halftone structure (myHalfTone), the code sets all of its values, including the background color and the dot color in HSV color space. The tint type selected, however, is luminance tint, meaning that only the lightness of the input color is used to calculate the proportion of dot and background to use for the halftone. The attributes specify a grayscale view port, meaning that the dot and background colors are also drawn in gray.

Listing 7-1 Changing a view port's dither, halftone, and attributes

gxViewPort  myViewPort
gxHalftone  myHalfTone;

GXSetViewPortAttributes(myViewPort, gxGrayPort);
GXSetViewPortDither (myViewPort, 4);

myHalfTone.angle = ff(6);
myHalfTone.frequency = ff(24);
myHalfTone.method = gxDispersedDot;
myHalfTone.tinting = gxLuminanceTint;
myHalfTone.tintSpace = gxHSVSpace;   




myHalfTone.backgroundColor.space = gxHSVSpace;
myHalfTone.backgroundColor.profile = nil;
myHalfTone.backgroundColor.element.hsv.value = 0xFFFF;
myHalfTone.backgroundColor.element.hsv.saturation = 0xCCCD;
myHalfTone.backgroundColor.element.hsv.hue = 0x8000;

myHalfTone.dotColor.space = gxHSVSpace;
myHalfTone.dotColor.profile = nil;
myHalfTone.dotColor.element.hsv.value = 0xFFFF;
myHalfTone.dotColor.element.hsv.saturation = 0xAD1C;
myHalfTone.dotColor.element.hsv.hue = 0xE4F9;

GXSetViewPortHalftone(myViewPort, &myHalfTone);
Note
Dithers and halftones are mutually exclusive. The halftone in this example overrides the dither, so dithering is not performed at drawing.
The GXGetViewPortDither function is described on page 7-80; the GXSetViewPortDither function is described on page 7-80.

The GXGetViewPortHalftone function is described on page 7-81; the GXSetViewPortHalftone function is described on page 7-82.

The GXGetViewPortAttributes function is described on page 7-89; the GXSetViewPortAttributes function is described on page 7-90.

Getting and Setting a View Port's View Group

Listing 7-2 demonstrates the use of the GXSetViewPortViewGroup function. It is part of a routine that creates an offscreen view group (newGroup) that is a copy of an existing view group (group). For each view port in the onscreen view group, the routine creates a copy. It then uses GXSetViewPortViewGroup to assign the proper view group to the new view port. (Listing 7-10 on page 7-54 shows another part of the same routine.)

The routine uses the count variable to decrement through the list of view ports (oldList, retrieved through two consecutive calls to GXGetViewGroupViewPorts) belonging to the onscreen view group. The code simultaneously builds, for its own purposes, a list (newList) of view ports for the offscreen view group, using GXCopyToViewPort and GXSetViewPortViewGroup to copy each view port into the offscreen view group and set its view group property.

Listing 7-2 Copying the view ports from one view group to another

long           portCount = GXGetViewGroupViewPorts(group, nil);
long           count = portCount;
gxViewPort     *oldPortList = (void *)NewPtr(portCount * 
                              sizeof(gxViewPort));
gxViewPort     *oldList = oldPortList;
gxViewPort     *newPortList = (void *)NewPtr(portCount * 
               sizeof(gxViewPort));
gxViewPort     *newList = newPortList;
GXGetViewGroupViewPorts(group, oldPortList);
while (count-- > 0)
   GXSetViewPortViewGroup(*newList++ = GXCopyToViewPort(nil, 
                           *oldList++), newGroup);
The GXGetViewPortViewGroup function is described on page 7-88. The GXSetViewPortViewGroup function is described on page 7-88.

Getting and Setting a View Port's Tag References

You can examine the list of references to tag objects currently associated with a view port object using the GXGetViewPortTags function. Once you create a tag object, you can attach it to a view port object using the GXSetViewPortTags function. You can attach as many tag objects as you like to a view port object.

Tag objects and the basic functions for manipulating them are described in the chapter "Tag Objects" in this book. That chapter also lists the common tag types defined and reserved by Apple Computer, Inc.

The GXGetViewPortTags function is described on page 7-91. The GXSetViewPortTags function is described on page 7-92.

Getting and Setting a View Port's Clip and Mapping

The clip and mapping properties of a view port control the visibility and location of its contents. For onscreen view ports attached to Macintosh windows, you do not directly set the clip or mapping properties; you move or resize the window with Window Manager calls, and QuickDraw GX automatically updates the view port's clip and mapping. For child view ports of window view ports, however, and for all offscreen view ports, you must set the clip and mapping yourself.

You use the functions GXGetViewPortMapping, and GXSetViewPortMapping to set a view port mapping to move the contents of the view port, such as when scrolling. You also set the view port mapping to provide scaled, rotated, or otherwise altered views of the view port's contents. Listing 7-3 shows an example that uses those functions plus GXGetViewPortClip and GXScaleMapping to scale the view port myViewPort
to 200 percent of its original size, about an origin at the center of the view port's clip.

Listing 7-3 Changing a view port's mapping

gxViewPort     myViewPort
gxMapping      myViewPortMapping;
gxShape        myViewPortFrame;
gxPoint        center;

GXGetViewPortMapping(myViewPort, &myViewPortMapping);
myViewPortFrame = GXGetViewPortClip(myViewPort);
GXGetShapeCenter(myViewPortFrame, 0L, &center);
GXScaleMapping(&myViewPortMapping, ff(2), ff(2), 
               center.x, center.y);
GXSetViewPortMapping(myViewPort, &myViewPortMapping);
GXDisposeShape(myViewPortFrame);
Note that, because the GXGetViewPortClip function creates a shape object, the code in Listing 7-3 disposes of the shape after using it. The GXGetShapeBounds function is described in the geometric operations chapter of Inside Macintosh: QuickDraw GX Graphics; the GXSetShapeMapping function is described in the mathematics chapter of Inside Macintosh: QuickDraw GX Environment and Utilities.

Getting the global mapping
If a view port is a child view port in a hierarchy, its mapping converts from local space into the local space of its parent view port, not directly into global space. If you want to determine the resultant mapping obtained by concatenating the mappings of a view port and all its parents--a mapping from local space all the way into global space--you can use GXGetViewPortGlobalMapping, which is described on page 7-79. For an example of its use, see Listing 7-11 on page 7-57.
You can use the GXSetViewPortClip function to set a view port clip to initialize or change the visible area of the view port. Listing 7-4 is a routine that sets up the clip of a child view port (gcontentViewPort) whose parent is the root view port attached to a Macintosh window (theWindow). The routine makes the clip the same size as the window's content area, minus the width of the scroll bars on the window's side and bottom.

The listing uses the application-defined function GetWindowBoundsShape to determine the rectangle shape corresponding to the content area of the window. That function retrieves a QuickDraw rectangle corresponding to the port rectangle of the window, and then converts it to a QuickDraw GX rectangle using the GXConvertQDPoint function.

Listing 7-4 Setting a view port clip

void ResetContentViewPortClip (WindowPtr theWindow)
{
   gxRectangle    viewRect;
   gxShape        contentViewPortClipShape;

   /* get the size of the window port rect */
   GetWindowBoundsShape(theWindow, &viewRect);

   /* Adjust the rectangle to accommodate the scroll bars */
   viewRect.right -= ff(kScrollBarWidth - 1);
   viewRect.bottom -= ff(kScrollBarWidth - 1);

   /* assign it as the clip shape */
   contentViewPortClipShape = GXNewRectangle(&viewRect);
   GXSetViewPortClip(gcontentViewPort, contentViewPortClipShape);
   GXDisposeShape (contentViewPortClipShape);
}
The GXConvertQDPoint function is described in the Macintosh environment chapter
of Inside Macintosh: QuickDraw GX Environment and Utilities. The GXNewRectangle function, which creates a rectangle shape, is described in the geometric shapes chapter of Inside Macintosh: QuickDraw GX Graphics.

The GXGetViewPortClip function is described on page 7-74; the GXSetViewPortClip function is described on page 7-75.
The GXGetViewPortMapping function is described on page 7-77; the GXSetViewPortMapping function is described on page 7-78.

Setting Up the View Port Hierarchy for a Window

Setting up a view port hierarchy means assigning the appropriate parent view port and child view port references to all view ports involved. The functions you use are GXGetViewPortParent, GXSetViewPortParent, GXGetViewPortChildren, and GXSetViewPortChildren. Take these steps to set up a simple hierarchy in which a child view port is used for drawing a window's content:

  1. Create the child view port in the window view port's view group.
  2. Create a clip shape and assign it to the child view port. Set the child view port's mapping.
  3. Assign the window view port as the parent of the child view port.
  4. Dispose of the clip shape.

Note that you do not have to add the child view port to the window view port's
list of children; when you set the parent view port property of the child view port, QuickDraw GX adds the child view port to the parent's list of child view ports.

Listing 7-5 is an example that sets up such a hierarchy. It creates a view port with the GXNewViewPort function, and uses GXGetViewPortViewGroup to find out what view group to assign it to. The code assigns properties to the view port with GXSetViewPortClip, GXSetViewPortMapping, and GXSetViewPortParent. The window view port is windowParentViewPort, and the rectangle viewRect defines a clipping area that is the size of the window minus the area reserved for scroll bars.

Listing 7-5 Setting up a view port for a window

gxRectangle    viewRect;
gxViewPort     windowParentViewPort;
gxShape        contentViewPortShape;
.  /*
.     Create window view port with GXNewWindowViewPort. Make 
.     viewRect equal to port rectangle minus scroll bars.
   */
gcontentViewPort = GXNewViewPort
                  (GXGetViewPortViewGroup(windowParentViewPort));
contentViewPortShape = GXNewRectangle(&viewRect);
GXSetViewPortClip(gcontentViewPort, contentViewPortShape);
GXSetViewPortMapping(gcontentViewPort, nil);
GXSetViewPortParent(gcontentViewPort, windowParentViewPort);
GXDisposeShape (contentViewPortShape);
Once you have set up a hierarchy, if you want to draw into the child view port--and thus onscreen--you must place a reference to the child view port in a transform object's view port list, and the shapes you draw must reference that transform.

The GXGetViewPortParent function is described on page 7-84; the GXSetViewPortParent function is described on page 7-84.
The GXGetViewPortChildren function is described on page 7-86; the GXSetViewPortChildren function is described on page 7-87.

Supporting Scrolling in a Window

To support scrolling in a view port attached to a Macintosh window, you need to create a child view port of the window view port, and draw into it rather than into its parent. QuickDraw GX prevents you from changing the mapping or clip of a view port directly attached to a Macintosh window.

When the user scrolls the window, you manipulate the child view port's mapping to scroll the content. When the user resizes the window, you manipulate the child view port's clip to fit the new window shape. When the user moves the window, you do nothing; QuickDraw GX takes care of positioning both parent and child view ports.

Listing 7-6 is an example of a scrolling routine that scrolls the contents of a child view port (gcontentViewPort) by specified vertical and horizontal amounts (hScroll
and vScroll), in response to mouse-down events in the scroll bars of a window (theWindow). The event-dispatching routine calls this scrolling routine after it has calculated how much scrolling is required. After the scrolling routine executes, a separate routine (not shown) updates the appearance of the scroll bars.

Listing 7-6 Supporting scrolling in a child view port

void DoScroll(WindowPtr theWindow, short hScroll, short vScroll)
{
   Rect        scrollRect;
   Point       scrollPt;
   RgnHandle   myRgn;
   gxMapping   viewPortMapping;
   
   if ((hScroll == 0) && (vScroll == 0)) return;

   /* 
      Get the child view port's mapping, adjust it for the 
      horizontal and vertical scroll, then reassign it to the 
      view port. The next drawing action will then reflect the 
      scrolled positions of shapes in the view port.
   */
   GXGetViewPortMapping(gcontentViewPort, &viewPortMapping);
   MoveMapping(&viewPortMapping, ff(hScroll), ff(vScroll));
   GXSetViewPortMapping(gcontentViewPort, &viewPortMapping);

   /* 
      Shift the pixels representing the already drawn contents of 
      window, so that less will have to be drawn when the window 
      is updated. Only the parts scrolled into view (specified by 
      the update region myRgn) will need to be redrawn. 
   */
   scrollRect = theWindow->portRect;
   scrollRect.right -= (kScrollBarWidth-1);
   scrollRect.bottom -= (kScrollBarWidth-1);

   SetPort(theWindow);
   myRgn = NewRgn();
   ScrollRect(&scrollRect, hScroll, vScroll, myRgn);


   /* update origin position in app's extended window record */
   SetPt(&scrollPt, hScroll, vScroll);
   AddPt(scrollPt, &((MyWindowPeek)theWindow)->origin);

   /* redraw the window and dispose of the region handle */
   DrawWindow(theWindow);
   DisposeRgn(myRgn);
}

Identifying a View Port's View Devices

The GXGetViewPortViewDevices function returns a list of all view devices that could be affected by shapes drawn in a given view port. Within the view group of the view port, all view device objects whose clip areas overlap the clip area of the view port appear in the list returned by this function.

You can use GXGetViewPortViewDevices to determine whether a given view device can be affected by drawing into a given view port. You can also use it to examine the properties of all devices that you might draw to, perhaps in order to assign appropriate properties to offscreen view devices.

Listing 7-7 is a library function (SetShapeFastXorTransfer) that uses another library function (SetInkFastXorTransfer) to set up a shape's color and an XOR transfer mode so that drawing with that color will cause a specified highlight color to replace the background color in the destination. The SetInkFastXorTransfer function is not shown here. Listing 7-7 is shown because it uses GXGetViewPortViewDevices to get a list of the view devices a view port can draw to, although it actually uses only the first view device in the list.

Listing 7-7 Setting a shape color for XOR highlighting

void SetShapeFastXorTransfer(gxShape source, gxColor *background, 
                              gxColor *result)
{
   long           viewPortCount, viewDeviceCount;
   void           *buffer;
   gxViewPort     vp;
   gxViewDevice   vd;
   gxInk          inky;

   /* get size of view port list, then allocate buffer for it */
   viewPortCount = GXGetTransformViewPorts(
                              GXGetShapeTransform(source), nil);
   buffer = NewPtr(sizeof(gxViewPort) * viewPortCount);


   /* check for memory error (not shown), then get list itself */
   GXGetTransformViewPorts(GXGetShapeTransform(source), 
                           (gxViewPort *)buffer);
    
   /* get no. of view devices, then allocate buffer for list */
   viewDeviceCount = GXGetViewPortViewDevices(
                              vp = *(gxViewPort *)buffer, nil);
.
.  /* check for memory error (not shown), then get list itself */
.
   GXGetViewPortViewDevices(vp, (gxViewDevice *)buffer);
   vd = *(gxViewDevice *)buffer;
   DisposePtr(buffer);

   /* get shape's ink; if shared, assign a copy of the ink */
   if (GXGetInkOwners(inky = GXGetShapeInk(source)) > 1) 
   {
      GXSetShapeInk(source, inky = GXNewInk());
      GXDisposeInk(inky);
   }
   /* set ink's transfer mode and suppress dithering */
   SetInkFastXorTransfer(inky, vd, vp, background, result);
   GXSetShapeInkAttributes(source, 
                           GXGetShapeInkAttributes(source) | 
                           gxSuppressDitherInk);
}
The GXGetViewPortViewDevices function is described on page 7-94.

Identifying a Shape's View Ports

The GXGetShapeGlobalViewPorts function returns a list of all view ports that a shape could actually appear in if it were drawn. If a shape's transform references a view port, and if that view port's clip does not totally exclude the shape from the visible part of the view port, the view port appears in the list returned by this function.

You can use GXGetShapeGlobalViewPorts to avoid the overhead of drawing shapes that cannot be visible. You can also use it as an input to the GXGetShapeGlobalViewDevices function to determine all the devices on which a given shape can appear.

The GXGetShapeGlobalViewPorts function is described on page 7-95. The GXGetShapeGlobalViewDevices function is described on page 7-115.

Measuring a Shape in Local Space

The GXGetShapeLocalBounds function measures the bounding rectangle of a shape in local coordinates--that is, after the transform mapping has been applied to the shape geometry. You can use GXGetShapeLocalBounds to compare the positions and sizes of two shapes in the same view port, even if they do not share the same transform
object. (To compare the positions and sizes of two shapes in different view ports, use
the GXGetShapeGlobalBounds function; to measure a shape on a view device, use GXGetShapeDeviceBounds.)

Listing 7-8 is a function in a shape-editing program. It draws a gray box representing
the bounding rectangle for each shape in a list of shapes passed to it. It calls GXGetShapeLocalBounds for each shape, and then defines and draws a rectangle shape whose geometry matches that bounding rectangle. Regardless of how each shape has been modified by its own transform mapping, GXGetShapeLocalBounds returns a rectangle (whose default transform has an identity mapping) that exactly matches the transformed shape's bounding rectangle when drawn in the view port.

Listing 7-8 makes use of the library function SetShapeCommonColor to set the bounding rectangle's color.

Listing 7-8 Locating the bounding rectangles of a list of shapes in a view port

void ShowLocalBounds(gxShape *p1stShape, long shapeCount)
{
   register gxShape     *pShape, rectShape;
   gxRectangle          bounds;

   pShape = p1stShape + shapeCount - 1;   /* no. of last shape */

   /* define a framed gray rectangle shape for the bounds */
   rectShape = GXNewShape(gxRectangleType);
   GXSetShapeFill(rectShape, gxClosedFrameFill);
   SetShapeCommonColor(rectShape, gxGray);

   /* go through shape list, get and draw local bounds for each */
   while (shapeCount--)
   {
      GXGetShapeLocalBounds(*pShape--, &bounds);
      GXSetRectangle(rectShape, &bounds);
      GXDrawShape(rectShape);
   }
   GXDisposeShape(rectShape);
}
The GXGetShapeLocalBounds function is described on page 7-96.

The GXGetShapeGlobalBounds function is described on page 7-125; the GXGetShapeDeviceBounds function is described on page 7-116.


Previous Book Contents Book Index Next

© Apple Computer, Inc.
7 JUL 1996